﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Diagnostics;
using Snowdreamist;
using Snowdreamist.Collection;
using CompositionPlugin.Service.Primitives;

namespace CompositionPlugin.Service
{
    public class DataTemplateManager : IDataTemplateManager
    {
        public DataTemplateManager() { }

        public virtual void Initialize()
        {
            // get all exported data template
            var lazyDTs = SystemRuntime.Current.Composition.Container.GetExports<IDataTemplateFactory, IDataTemplateExportMetadata>();
            if (lazyDTs != null)
            {
                foreach (var dt in lazyDTs)
                    try
                    {
                        this.Add(dt.Metadata.TargetTypeName, dt.Metadata.ViewState, dt.Value);
                    }
                    catch (ValueExistsException)
                    {
                        SystemRuntime.Current.Log.Write(TraceEventType.Warning, 0, "DataTemplateManager detects exported conflict for type " + dt.Metadata.TargetTypeName + " viewState " + dt.Metadata.ViewState);
                    }
                    catch (Exception error)
                    {
                        SystemRuntime.Current.Log.Write(TraceEventType.Warning, 0, "DataTemplateManager detects error when importing" + error.ToString());
                    }
            }
        }

        public DataTemplate GetTemplate(Type dataType)
        {
            return this.GetTemplate(dataType, string.Empty);
        }

        public DataTemplate GetTemplate(string dataTypeName)
        {
            return this.GetTemplate(dataTypeName, string.Empty);
        }

        public DataTemplate GetTemplate(Type dataType, string viewState)
        {
            if (dataType == null)
                throw new ArgumentNullException("dataType");
            return this.GetTemplate(dataType.FullName, viewState);
        }

        public DataTemplate GetTemplate(string dataTypeName, string viewState)
        {
            if (string.IsNullOrWhiteSpace("dataTypename"))
                throw new ArgumentException("Invalid dataTypeName");
            if(viewState == null)
                viewState = string.Empty;

            List<DataTemplateWrapper> lst = null;

            if (this._dataTemplates.TryGetValue(dataTypeName, out lst))
            {
                if (lst == null)
                    return null;
                var wrapper = lst.Where(p => p.ViewState == viewState).FirstOrDefault();
                if (wrapper == null || wrapper.Factory == null)
                    return null;
                else
                    return wrapper.Factory.Create(dataTypeName, viewState);
            }
            else
            {
                return null;
            }
        }

        public virtual void OnRuntimeDisposing() { }

        public void Add(string dataTypename, IDataTemplateFactory factory)
        {
            this.Add(dataTypename, string.Empty, factory);
        }

        public void Add(Type dataType, IDataTemplateFactory factory)
        {
            this.Add(dataType, string.Empty, factory);
        }

        public void Add(string dataTypename, string viewState, IDataTemplateFactory factory)
        {
            if(string.IsNullOrWhiteSpace(dataTypename))
                throw new ArgumentException("Invalid dataTypename");
            if(factory == null)
                throw new ArgumentNullException("factory");
            var lst = this._dataTemplates.GetOrAdd(dataTypename, () => new List<DataTemplateWrapper>());
            if (lst == null)
                throw new InvalidOperationException("Null list");
            lock (lst)
            {
                if (lst.Where(p => p.ViewState == viewState).Count() != 0)
                    throw new ValueExistsException();
                lst.Add(new DataTemplateWrapper(viewState, factory));
            }
        }

        public void Add(Type dataType, string viewState, IDataTemplateFactory factory)
        {
            if (dataType == null)
                throw new ArgumentNullException("dataType");
            this.Add(dataType.FullName, viewState, factory);
        }

        #region private

        private readonly ThreadSafeDictionary<string, List<DataTemplateWrapper>> _dataTemplates = new ThreadSafeDictionary<string, List<DataTemplateWrapper>>();

        #endregion
    }
}
